#ifndef TC_DEBUG_HH
#define TC_DEBUG_HH

/** \file Debug.hh 
 * Declarations for Debug class
 *
 * (c) Copyright  Semih Cemiloglu
 * All rights reserved, see COPYRIGHT file for details.
 *
 * $Id: Log.hh,v 1.3 2008/10/09 04:51:41 bdssadm Exp $
 *
 *
 */

// Standard headers
#include <iosfwd>
#include <sstream>
#include <stdio.h>
// Project headers
#include "Global.hh"

namespace TC {

//! To turn on or off debugging code completely
const bool DebugEnabled = 
#if TC_NODEBUG
  false;
#else
  true;
#endif

class Debug {
  
  // FRIENDS
  friend class DebugTest;
  
 public:
  
  //! Frequently-used debug levels
  enum Levels {
    e_None  = 0,
    e_Fatal = 1,
    e_Error = 2,
    e_Warn  = 3,
    e_Info  = 4,
    e_Debug = 5,
    e_DebugLo = 6,
    e_DebugMe = 7,
    e_DebugHi = 8
  };
    
  // CREATORS
  //! Construct Debug instance with a nominated level
  //! file name, location
  Debug(int level, const char* file, int line);
  //! Destroy Debug object which flushes accumulated logs
  //! during objects lifetime to the Debug sink.
  ~Debug();
      
  // ACCESSORS

  //! Is this debug object writable to sink?
  bool isWritable() const
  { return (m_level >= s_Level) ? true : false; }
  
  //! Get debug stream for output
  std::ostringstream& getStream()
  { return m_os; }
      
  // STATIC FUNCTIONS
  //! Get current log level, application wide
  static int  GetLogLevel()
  { return s_Level; }

  //! Get current log level, application wide
  static void SetLogLevel(int level)
  { s_Level = level; }

  //! Set precise timing flag which will force log messages to contain
  //! sub-second timestamps.
  static void SetPreciseTiming(bool flag)
  { s_PreciseTiming = flag; }

  //! Get current sink file used for logging
  static FILE* GetSinkFile();

  //! Set current sink file for logging.
  //! Debug class doesn't take the ownership, ust writes to it.
  static void SetSinkFile(FILE* fs);

  //! Get char code for a given level (\see Levels)
  static char GetLevelCode(int level);
      
 protected:
  
  // MODIFERS
  //! Add prefix string for this debug object's log message.
  //! Such as Timestamp, level char etc.
  void addPrefix();

  //! Add timestamp to the debug stream
  void addTimestamp();
      
 private:

  // NOT ALLOWED
  Debug();
  Debug(const Debug&);
  Debug& operator=(const Debug&);
      
  // MEMBERS
  //! Level of this debug object
  int m_level;
  //! File name of the source code that instantiated this object
  const char* m_file;
  //! File location (line number) that instantiated this object
  const int m_line;
  
  //! Stream object to hold the log until destruction
  std::ostringstream m_os;
        
  // STATIC MEMBERS
  //! Currently accepted level application wide
  static TC_VOLATILE int s_Level;
      
  //! Flag for precise timing up to milli second
  static TC_VOLATILE bool s_PreciseTiming;

  //! Flag for precise location of message of the debug log
  static TC_VOLATILE bool s_PreciseLocation;

  //! Current sink file stream
  static FILE* s_Sink;      

};  
    

} // end namespace


#define TC_LOG_WRITE(Lvl, Msg)                                  \
  do {                                                          \
    if(::TC::DebugEnabled && Lvl <= ::TC::Debug::GetLogLevel()) \
      ::TC::Debug(Lvl,__FILE__,__LINE__).getStream()            \
            << Msg << std::endl;                                \
  } while(0)


#define TC_LOG_PRINTF(Fmt, ...)               \
  do {                                        \
    if(::TC::DebugEnabled) {                  \
      FILE* f = ::TC::Debug::GetSinkFile();   \
      fprintf(f, Fmt, __VA_ARGS__);           \
    }                                         \
  } while(0)


#define TC_LOG_EX(Exc)                                                  \
  do {                                                                  \
    if(::TC::DebugEnabled &&                                            \
       ::TC::Debug::e_Error <= ::TC::Debug::GetLogLevel()) {            \
      ::TC::Debug log(::TC::Debug::e_Error,__FILE__,__LINE__);          \
      std::ostream& os = log.getStream();                               \
      Exc.print(os);                                                    \
      os << std::endl;                                                  \
    }                                                                   \
  } while(0)


#if TC_NODEBUG
# define TC_LOG_FATAL(E)    ((void)0)
# define TC_LOG_ERROR(E)    ((void)0)
# define TC_LOG_WARN(E)     ((void)0)
# define TC_LOG_INFO(E)     ((void)0)
# define TC_LOG_DEBUG(E)    ((void)0)
# define TC_LOG_LDEBUG(E)   ((void)0)
# define TC_LOG_MDEBUG(E)   ((void)0)
# define TC_LOG_HDEBUG(E)   ((void)0)
#else
# define TC_LOG_FATAL(E)    TC_LOG_WRITE(::TC::Debug::e_Fatal,E)
# define TC_LOG_ERROR(E)    TC_LOG_WRITE(::TC::Debug::e_Error,E)
# define TC_LOG_WARN(E)     TC_LOG_WRITE(::TC::Debug::e_Warn,E)
# define TC_LOG_INFO(E)     TC_LOG_WRITE(::TC::Debug::e_Info,E)
# define TC_LOG_DEBUG(E)    TC_LOG_WRITE(::TC::Debug::e_Debug,E)
# define TC_LOG_LDEBUG(E)   TC_LOG_WRITE(::TC::Debug::e_DebugLo, E)
# define TC_LOG_MDEBUG(E)   TC_LOG_WRITE(::TC::Debug::e_DebugMe, E)  
# define TC_LOG_HDEBUG(E)   TC_LOG_WRITE(::TC::Debug::e_DebugHi, E)  
#endif


#endif /* Include guard */
